﻿using System;
using System.Collections.Generic;
using MentalAlchemy.Molecules.MachineLearning;

namespace MentalAlchemy.Molecules.NevaAlgorithm.nevamod
{
	public class Neva2Ind : NevaInd
	{
		public List<NevaIndChanges> Changes = new List<NevaIndChanges>();
		public string LastMutation = "";	// the last mutation operator, which was applied to the individual.

		#region - Construction. -
		// General invariants:
		// [Activations] It is guaranteed that dictionary of activations is properly created or copied from
		//				 the argument for hidden and output nodes.

		public Neva2Ind()
		{
			Activations = new Dictionary<int, ActivationFunction>();
			//// set default activations for output nodes.
			//foreach (var id in ind.Network.OutputIds)
			//{
			//    Activations.Add(id, Network.DefaultOutputActivation);
			//}

			//// set default activations for hidden nodes.
			//foreach (var id in ind.HiddenNodesIndices)
			//{
			//    Activations.Add(id, Network.DefaultActivationFunction);
			//}
		}

		public Neva2Ind(Neva2Ind ind) : base(ind)
		{
			Changes = new List<NevaIndChanges>(ind.Changes);
			//Activations = new Dictionary<int, ActivationFunction>(ind.Activations);
			LastMutation = ind.LastMutation;
			Activations = new Dictionary<int, ActivationFunction>();
			foreach (var pair in ind.Activations)
			{
				Activations.Add(pair.Key, pair.Value);
			}
		}

		public Neva2Ind(List<int> inputs, List<int> outputs, float weightRange, float weightMin, Random rng) : base(inputs, outputs, weightRange, weightMin, rng)
		{
			Activations = new Dictionary<int, ActivationFunction>();

			// init by default activations.
			for (int i = 0; i < outputs.Count; i++)
			{
				Activations.Add(outputs[i], Network.DefaultOutputActivation);
			}

			// no need for setting activations for hidden nodes (no hidden nodes, pals).
		}
		#endregion

		#region - Static methods. -
		public static Neva2Ind Create (NevaInd ind)
		{
			var res = new Neva2Ind();
			res.Assign(ind);

			res.Activations = new Dictionary<int, ActivationFunction>();

			// set default activations for output nodes.
			foreach (var id in ind.Network.OutputIds)
			{
				res.Activations.Add(id, res.Network.DefaultOutputActivation);
			}

			// set default activations for hidden nodes.
			foreach (var id in ind.HiddenNodesIndices)
			{
				res.Activations.Add(id, res.Network.DefaultActivationFunction);
			}

			return res;
		}
		#endregion

		#region - Public methods. -
		public override AbstractIndividual Clone()
		{
			return new Neva2Ind(this);
		}
		
		public void RegisterChanges(NevaIndChanges ch)
		{
			Changes.Add(ch);
		}

		public override void Assign(NevaInd ind)
		{
			base.Assign(ind);

			if (ind is Neva2Ind)
			{
				Changes = new List<NevaIndChanges>(((Neva2Ind)ind).Changes);
			}
			LastMutation = ((Neva2Ind)ind).LastMutation;
		}

		#region - Utility methods. -
		/// <summary>
		/// [molecule]
		/// 
		/// Checks activations for consistency.
		/// Doesn't consider isolated nodes.
		/// Returns [false] if something is wrong.
		/// </summary>
		/// <returns></returns>
		public bool TestActivations ()
		{
			foreach (var pair in Activations)
			{
				var node = Network.GetNode(pair.Key);
				if (node == null) continue;

				if (node.ActivationFunction != pair.Value)
				{
					return false;
				}
			}
			return true;
		}
		#endregion
		#endregion
	}
}
